﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using System.IO;

namespace UmlDesigner.Model
{
    class ModelSerializer
    {

        internal void Save(Project documentSet, Stream stream)
        {
            Storage s = Storage.BeginSave(stream);

            s.SaveInt(documentSet.Documents.Count);

            foreach (Document d in documentSet.Documents)
            {
                Save(d, s);
            }

            s.Close();
        }

        internal void Save(Document doc, Storage s)
        {
            Dictionary<ItemWidget, int> storedItems = new Dictionary<ItemWidget, int>();
            int currentItem = 1;

            s.SaveString(doc.Name);

            s.SaveInt(doc.DocumentSize.Width);
            s.SaveInt(doc.DocumentSize.Height);

            s.SaveInt(doc.GetNumClasses());
            foreach (ItemWidget item in doc.items)
            {
                if (item.IsClass())
                {
                    storedItems[item] = currentItem++;
                    SaveClassToStorage((ClassWidget)item, s);
                }
            }

            s.SaveInt(doc.GetNumNotes());
            foreach (ItemWidget item in doc.items)
            {
                if (item.IsNote())
                {
                    storedItems[item] = currentItem++;
                    SaveNoteToStorage((NoteWidget)item, s);
                }
            }

            s.SaveInt(doc.GetNumFolders());
            foreach (ItemWidget item in doc.items)
            {
                if (item.IsFolder())
                {
                    storedItems[item] = currentItem++;
                    SaveFolderToStorage((FolderWidget)item, s);
                }
            }


            // The tricky part, saving the links.
            s.SaveInt(doc.GetNumLinks());
            foreach (ItemWidget item in doc.items)
            {
                if (item.IsLink())
                {
                    storedItems[item] = currentItem++;
                    LinkWidget link = (LinkWidget)item;

                    // Save tokens to the source and destination.
                    s.SaveInt(storedItems[link.GetSource()]);
                    s.SaveInt(storedItems[link.GetDestination()]);

                    // Now save the link details.
                    SaveLinkDetailsToStorage(link, s);
                }
            }
        }

        internal void Load(Project documentSet, Stream stream)
        {
            documentSet.Documents.Clear();
            Storage s = Storage.Open(stream);

            int numDocs;
            if (s.version > 1)
            {
                numDocs = s.LoadInt();
            }
            else
            {
                // Version 1 only stored a single document.
                numDocs = 1;
            }

            while (numDocs-- != 0)
            {
                Document d = new Document();
                Load(d, s);
                documentSet.Documents.Add(d);
            }

            s.Close();
        }


        internal void Load(Document doc, Storage s)
        {
            Dictionary<int, ItemWidget> loadedItems = new Dictionary<int, ItemWidget>();
            int currentItem = 1;

            if (s.version > 1)
            {
                doc.Name = s.LoadString();
            }

            int docWidth = s.LoadInt();
            int docHeight = s.LoadInt();
            doc.DocumentSize = new Size(docWidth, docHeight);

            int numClasses = s.LoadInt();
            while (numClasses-- != 0)
            {
                ClassWidget item = new ClassWidget();
                loadedItems[currentItem++] = item;
                LoadClassFromStorage(item, s);
                doc.items.Add(item);
            }

            int numNotes = s.LoadInt();
            while (numNotes-- != 0)
            {
                NoteWidget item = new NoteWidget();
                loadedItems[currentItem++] = item;
                LoadNoteFromStorage(item, s);
                doc.items.Add(item);
            }

            int numFolders = s.LoadInt();
            while (numFolders-- != 0)
            {
                FolderWidget item = new FolderWidget();
                loadedItems[currentItem++] = item;
                LoadFolderFromStorage(item, s);
                doc.items.Add(item);
            }

            int numLinks = s.LoadInt();
            while (numLinks-- != 0)
            {
                ItemWidget src = loadedItems[s.LoadInt()];
                ItemWidget dest = loadedItems[s.LoadInt()];

                LinkWidget item = new LinkWidget(src, dest);
                loadedItems[currentItem++] = item;

                LoadLinkDetailsFromStorage(item, s);

                // Utter the magic incantations to generate a real link...
                src.AddLinkToDest(item);
                dest.AddLinkFromSource(item);
                item.AllocateSlots();
                item.RecalculateAllSlots();

                doc.items.Add(item);
            }
        }


        #region Class Serialization
        private void LoadClassFromStorage(ClassWidget classWidget, Storage s)
        {
            classWidget.name = s.LoadString();

            if (s.version > 1)
            {
                classWidget.GenerateCodeForItem = s.LoadBool();
            }

            int x = s.LoadInt();
            int y = s.LoadInt();
            classWidget.SetClassPosition(new Point(x, y));

            int methodCount = s.LoadInt();
            while (methodCount-- != 0)
            {
                UmlMethod m = new UmlMethod();
                m.name = s.LoadString();
                m.returnType = s.LoadString();
                m.virtualModifier = s.LoadString();
                m.implementation = s.LoadString();
                m.accessLevel = s.LoadString();

                int argumentsCount = s.LoadInt();
                while (argumentsCount-- != 0)
                {
                    Argument a = new Argument();
                    a.type = s.LoadString();
                    a.name = s.LoadString();
                    m.arguments.Add(a);
                }
                classWidget.methods.Add(m);
            }

            int attributesCount = s.LoadInt();
            while (attributesCount-- != 0)
            {
                Attribute attr = new Attribute();
                attr.accessLevel = s.LoadString();
                attr.name = s.LoadString();
                attr.type = s.LoadString();
                classWidget.attributes.Add(attr);
            }
        }

        private void SaveClassToStorage(ClassWidget classWidget, Storage s)
        {
            s.SaveString(classWidget.name);

            s.SaveBool(classWidget.GenerateCodeForItem);

            s.SaveInt(classWidget.Position.X);
            s.SaveInt(classWidget.Position.Y);

            s.SaveInt(classWidget.methods.Count);
            foreach (UmlMethod m in classWidget.methods)
            {
                s.SaveString(m.name);
                s.SaveString(m.returnType);
                s.SaveString(m.virtualModifier);
                s.SaveString(m.implementation);
                s.SaveString(m.accessLevel);

                s.SaveInt(m.arguments.Count);
                foreach (Argument a in m.arguments)
                {
                    s.SaveString(a.type);
                    s.SaveString(a.name);
                }
            }

            s.SaveInt(classWidget.attributes.Count);
            foreach (Attribute attr in classWidget.attributes)
            {
                s.SaveString(attr.accessLevel);
                s.SaveString(attr.name);
                s.SaveString(attr.type);
            }
        }

        #endregion

        #region Note Serialization
        private void SaveNoteToStorage(NoteWidget noteWidget, Storage s)
        {
            s.SaveInt(noteWidget.Position.X);
            s.SaveInt(noteWidget.Position.Y);

            s.SaveString(noteWidget.noteContents);
        }

        private void LoadNoteFromStorage(NoteWidget item, Storage s)
        {
            int x = s.LoadInt();
            int y = s.LoadInt();
            item.SetClassPosition(new Point(x, y));

            item.noteContents = s.LoadString();
        }
        #endregion

        #region Folder Serialization
        private void SaveFolderToStorage(FolderWidget folderWidget, Storage s)
        {
            s.SaveInt(folderWidget.Position.X);
            s.SaveInt(folderWidget.Position.Y);

            s.SaveString(folderWidget.name);
        }

        private void LoadFolderFromStorage(FolderWidget item, Storage s)
        {
            int x = s.LoadInt();
            int y = s.LoadInt();
            item.SetClassPosition(new Point(x, y));

            item.name = s.LoadString();
        }
        #endregion


        private void SaveLinkDetailsToStorage(LinkWidget item, Storage s)
        {
            s.SaveString(item.AccessType);
            s.SaveString(item.CollectionClass);
            s.SaveBool(item.GenerateProperty);
            s.SaveString(item.Name);
            s.SaveInt((int)item.LinkType);
            s.SaveString(item.MultiplicityType);
        }

        private void LoadLinkDetailsFromStorage(LinkWidget item, Storage s)
        {
            item.AccessType = s.LoadString();
            item.CollectionClass = s.LoadString();
            item.GenerateProperty = s.LoadBool();
            item.Name = s.LoadString();
            item.LinkType = (LinkWidget.LineType)s.LoadInt();
            item.MultiplicityType = s.LoadString();
        }

    }
}
